﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using TexLib;
using OpenTK.Input;
using System.Threading;
using System.Drawing;
using OpenTK.Graphics.OpenGL;
using MyOpenGame.Entity;
using Entity;

namespace MyOpenGame
{
    public class Game : GameWindow
    {
        private volatile List<AModel> _modelObjects;

        private readonly GameUtility _gameUtility;
        private volatile static Game _instance;
        private Defence _selectedModel;
        private Inventory _inventory;
        private Player _player;

        public static Game getInstance()
        {
            if(_instance == null)
            {
                _instance = new Game(new Player("TEST"));
            }

            return _instance;
        }

        public Game(Player player): base(1280, 800, OpenTK.Graphics.GraphicsMode.Default, "OpenTK Quick Start Sample")
        {
            _instance = this;
            _player = player;
            _selectedModel = null;
            VSync = VSyncMode.On;
            _modelObjects = new List<AModel>();
            _gameUtility = new GameUtility();

            Mouse.ButtonUp += MouseClicked;
        }

        public void AddModel(AModel model)
        {
            lock (this)
            {
                _modelObjects.Add(model);
            }
        }

        public List<AModel> GetModels()
        {
            lock (this)
            {
                return _modelObjects;
            }
        }

        /// <summary>Load resources here.</summary>
        /// <param name="e">Not used.</param>
        protected override void OnLoad(EventArgs e)
        {
            OpenTK.Graphics.OpenGL.GL.ClearColor(0.1f, 0.5f, 0.5f, 0.3f);
            OpenTK.Graphics.OpenGL.GL.Enable(OpenTK.Graphics.OpenGL.EnableCap.Blend);
            OpenTK.Graphics.OpenGL.GL.BlendFunc(OpenTK.Graphics.OpenGL.BlendingFactorSrc.SrcAlpha, OpenTK.Graphics.OpenGL.BlendingFactorDest.OneMinusSrcAlpha);

            TexUtil.InitTexturing();
            // Load a bitmap from disc, and put it in a GL texture.
            Texture.setBackground(Texture.Load(Library.Properties.Resources.backgroundImage));
            Texture.setReideer(Texture.Load(Library.Properties.Resources.Reideer));
            Texture.setSource(Texture.Load(Library.Properties.Resources.Source));
            Texture.setSourceSelected(Texture.Load(Library.Properties.Resources.SourceSelected));
            Texture.setBuilding(Texture.Load(Library.Properties.Resources.Building));
            Texture.setBuildingSelected(Texture.Load(Library.Properties.Resources.BuildingSelected));
            Texture.setBall(Texture.Load(Library.Properties.Resources.Ball));
            Texture.setInventory(Texture.Load(Library.Properties.Resources.Inventory));

            Initialize();

            var gameLoopThread = new Thread(GameLoop);
            gameLoopThread.Start();
            gameLoopThread.IsBackground = true;
        }

        private void Initialize()
        {
            _inventory = new Inventory();
            lock(this)
            {
                _modelObjects.Add(_inventory);

                for (int i = 0; i < _gameUtility.GetDefenderCount(); i++)
                {
                    _modelObjects.Add(new Defence(i, new Source()));
                }
            }
        }

        /// <summary>
        /// Called when your window is resized. Set your viewport here. It is also
        /// a good place to set up your projection matrix (which probably changes
        /// along when the aspect ratio of your window).
        /// </summary>
        /// <param name="e">Not used.</param>
        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(new System.Drawing.Size(Width, Height));
            GL.MatrixMode(OpenTK.Graphics.OpenGL.MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(0, Width, 0, Height, -1, 1);
        }

        /// <summary>
        /// Called when it is time to setup the next frame. Add you game logic here.
        /// </summary>
        /// <param name="e">Contains timing information for framerate independent logic.</param>
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (Keyboard[Key.Escape])
                Exit();

            lock (this)
            {
                //an den eixa building
                _modelObjects.RemoveAll(item => item.GetCurrentPosition()[0].X < 0);
                _modelObjects.RemoveAll(item => item.GetCurrentPosition()[1].Y > 1280);
                _modelObjects.RemoveAll(item => 
                    {
                        if (item.GetLifePoints() <= 0)
                        {
                            if (item is Attack)
                            {
                                _player.AddGold(((Attack)item).GetGold());
                                return true;
                            }
                            if (item is IDefenceAttack)
                            {
                                return true;
                            }
                            return false;
                        }
                        else
                        {
                            return false;
                        }
                    });

                foreach (AModel item in _modelObjects)
                {
                    if (item is Ball)
                    {
                        foreach (AModel model in _modelObjects)
                        {
                            if (model.GetRow() == item.GetRow() && !(model is Defence) && !(model is Ball))
                            {
                                if (item.GetCurrentPosition()[1].X >= model.GetCurrentPosition()[0].X
                                    && item.GetCurrentPosition()[0].X <= model.GetCurrentPosition()[1].X)
                                {
                                    IDefenceAttack ball = (IDefenceAttack)item;
                                    ball.Destroy();
                                    model.RemoveLifePoints(1);
                                }
                            }
                        }
                    }
                }

            }

        }

        /// <summary>
        /// Called when it is time to render the next frame. Add your rendering code here.
        /// </summary>
        /// <param name="e">Contains timing information.</param>
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            OpenTK.Graphics.OpenGL.GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit);
            OpenTK.Graphics.OpenGL.GL.MatrixMode(OpenTK.Graphics.OpenGL.MatrixMode.Modelview);
            OpenTK.Graphics.OpenGL.GL.LoadIdentity();

            OpenTK.Graphics.OpenGL.GL.BindTexture(OpenTK.Graphics.OpenGL.TextureTarget.Texture2D, Texture.getBackground());
            OpenTK.Graphics.OpenGL.GL.Begin(OpenTK.Graphics.OpenGL.BeginMode.Quads);
            OpenTK.Graphics.OpenGL.GL.TexCoord2(0, 0); OpenTK.Graphics.OpenGL.GL.Vertex2(0, 800);
            OpenTK.Graphics.OpenGL.GL.TexCoord2(1, 0); OpenTK.Graphics.OpenGL.GL.Vertex2(1280, 800);
            OpenTK.Graphics.OpenGL.GL.TexCoord2(1, 1); OpenTK.Graphics.OpenGL.GL.Vertex2(1280, 0);
            OpenTK.Graphics.OpenGL.GL.TexCoord2(0, 1); OpenTK.Graphics.OpenGL.GL.Vertex2(0, 0);
            OpenTK.Graphics.OpenGL.GL.End();

            lock (this)
            {
                foreach (AModel model in _modelObjects)
                {
                    model.Draw();
                }
            }

            DrawPlayerInfo();

            SwapBuffers();
        }

        private void DrawPlayerInfo()
        {
            Font font = new Font(FontFamily.GenericSansSerif, 18.0f);
            OpenTK.Graphics.TextPrinter printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);

            printer.Begin();

            printer.Print(_player.GetName() + "   " + _player.GetGold(), font, Color.White, new RectangleF(1150, 20, 200, 50));
            printer.End();
        }

        public void MouseClicked(object sender, MouseButtonEventArgs e)
        {
            int x = Mouse.X;
            int y = Mouse.Y - 800;
            if (y < 0)
            {
                y = y * (-1);
            }

            lock (this)
            {
                //if inventory is clicked
                if (_inventory.GetCurrentPosition()[0].X <= x && _inventory.GetCurrentPosition()[2].X >= x
                    && _inventory.GetCurrentPosition()[0].Y >= y && _inventory.GetCurrentPosition()[2].Y <= y)
                {
                    DefenderState selectedInvDefence = _inventory.GetSelectedItem(new Point(x,y));
                    if (_player.Buy(selectedInvDefence.GetCost()))
                    {
                        _selectedModel.Upgrade(selectedInvDefence);
                    }
                }
                else
                {
                    bool isDefenceSelected = false;
                    bool changedSelectedState = false;
                    foreach (AModel model in _modelObjects)
                    {

                        if (model is Defence)
                        {
                            if (model.GetCurrentPosition()[0].X <= x && model.GetCurrentPosition()[2].X >= x
                                && model.GetCurrentPosition()[0].Y >= y && model.GetCurrentPosition()[2].Y <= y)
                            {
                                isDefenceSelected = true;

                                Defence defence = (Defence)model;
                                if (_selectedModel != null)
                                {
                                    _selectedModel.ChangeState();
                                }
                                else
                                {
                                    changedSelectedState = true;
                                }
                                _selectedModel = defence;
                                defence.ChangeState();

                                break;
                            }
                        }
                    }
                    if (isDefenceSelected && changedSelectedState)
                    {
                        _inventory.ChangeState();
                    }
                    else if (!isDefenceSelected)
                    {
                        if (_selectedModel != null)
                        {
                            _selectedModel.ChangeState();
                            _inventory.ChangeState();
                        }
                        _selectedModel = null;


                    }

                }
            }
        }

        public void GameLoop()
        {
            while (true)
            {
                Random random = new Random();
                int row = random.Next(0, 4);

                lock (this)
                {
                    _modelObjects.Add(new Reindeer(row));
                }

                int timeToSleep = random.Next(500, 5500);
                Thread.Sleep(timeToSleep);
            }
        }
        
    }
}
